﻿using System;
using System.Data;
using ServiciosGeograficos.Persistencia;
using System.Text.RegularExpressions;

namespace ServiciosGeograficos.Reconocimiento
{
    public class InterpreteCalles
    {
        private PostgresDAO postgresDAO;

        // Dado el nombre de una calle devuelve una lista de las calles similares
        private DataSet posiblesCalle(String nmCalle, Boolean esEsquina)
        {
            // Valido si el nombre de calle pasado como parametro no existe dentro de algun nombre de alguna calle
            String query = "select distinct calle.nombre as nm_calle from calles calle where ";
            query += " calle.nombre like '%" + nmCalle + "%'";
            DataSet dsCalles;
            if (esEsquina)
            {
                dsCalles = postgresDAO.ejecutarQuery(query, "posiblesEsquinas");
            }
            else
            {
                dsCalles = postgresDAO.ejecutarQuery(query, "posiblesCalles");
            }
            if (dsCalles.Tables["esquinas"] == null || dsCalles.Tables["esquinas"].Rows.Count == 0)
            {
                // Si no encontre nada, busco calles por similitud
                query = "select distinct calle.nombre as nm_calle, levenshtein(calle.nombre,'" + nmCalle.ToUpper() + "') as levenshtein from calles calle";
                query += " where levenshtein(calle.nombre,'" + nmCalle.ToUpper() + "') < 4 ";
                query += " order by levenshtein(calle.nombre,'" + nmCalle.ToUpper() + "') asc ";
                query += " limit 5";
                if (esEsquina)
                {
                    dsCalles = postgresDAO.ejecutarQuery(query, "posiblesEsquinas");
                }
                else
                {
                    dsCalles = postgresDAO.ejecutarQuery(query, "posiblesCalles");
                }
            }
            return dsCalles;
        }

        // Interpreta los nombres de calles y los normaliza
        public String normalizaCalles(String nmCalle)
        {

            nmCalle = normalizaCallesAvenida(nmCalle);
            nmCalle = normalizaCallesBoulevard(nmCalle);
            return nmCalle;
        }

        // Interpreta los nombres de Avenidas y los normaliza
        public String normalizaCallesAvenida(String nmCalle)
        {

            Regex avdaPattern = new Regex("(Avenida )|(Avda )|(Avda\\. )|(Av )|(Av\\. )", RegexOptions.IgnoreCase);

            if (avdaPattern.IsMatch(nmCalle))
            {
                String avda = avdaPattern.Replace(nmCalle, "");
                avda += ", AVENIDA";
                return avda;
            }
            return nmCalle;
        }

        // Interpreta los nombres de Boulevar y los normaliza
        public String normalizaCallesBoulevard(String nmCalle)
        {
            Regex bvrPattern = new Regex("(Boulevard )|(Bvr )|(Bulevar )|(Bulevard )|(Bvr\\. )|(Bv )|(Bv\\. )", RegexOptions.IgnoreCase);
            if (bvrPattern.IsMatch(nmCalle))
            {
                String bvr = bvrPattern.Replace(nmCalle, "");
                bvr += ", BOULEVARD";
                return bvr; 
            }
            return nmCalle;
        }

        // Dado el nombre de una calle y un numero devuelve true si encontro el punto, de lo contrario devuelve false
        private bool validarCalleNumero(String nmCalle, String numPuerta)
        {
            String query = "select distinct calle_puerta.nm_calle as nm_calle,calle_puerta.nm_esquina as nm_esquina,calle_puerta.punto_x as punto_x,calle_puerta.punto_y as punto_y from vw_calle_puerta calle_puerta ";
            query += " where calle_puerta.nm_calle like '%" + nmCalle.ToUpper() + "%'";
            query += " and calle_puerta.num_puerta = '" + numPuerta.ToUpper() + "'";
            DataSet dsEsquinas = postgresDAO.ejecutarQuery(query, "esquinas");
            return dsEsquinas.Tables["esquinas"].Rows.Count == 1;
        }

        // Dado el nombre de una calle y una esquina devuelve true si encontro el punto, de lo contrario devuelve false
        private bool validarCalleEsquina(String nmCalle, String nmEsquina)
        {
            String query = "select distinct calle.nombre as nm_calle,esquina.nombre as nm_esquina,x(intersection(calle.the_geom, esquina.the_geom)) as punto_x,y(intersection(calle.the_geom, esquina.the_geom)) as punto_y  from calles calle, calles esquina where intersects(calle.the_geom, esquina.the_geom)";
            query += " and calle.nombre like '%" + nmCalle.ToUpper() + "%'";
            query += " and esquina.nombre like '%" + nmEsquina.ToUpper() + "%'";
            query += " and geometrytype(intersection(calle.the_geom, esquina.the_geom)) = 'POINT'";
            DataSet dsEsquinas = postgresDAO.ejecutarQuery(query, "esquinas");
            return dsEsquinas.Tables["esquinas"].Rows.Count == 1;
        }


        // Dado el nombre (no necesariamente completo)  de una calle, devuelve en el dataset el/los nombres completo de la calle.
        private bool encontreCalle(String nmCalle)
        {
            String query = "select distinct calle.nombre from calles calle";
            query += " where calle.nombre = '" + nmCalle + "'";
            DataSet dsPosiblesCalles = postgresDAO.ejecutarQuery(query, "posiblesCalles");
            return dsPosiblesCalles.Tables["posiblesCalles"].Rows.Count == 1;
        }

        // Dado una calle y un numero de puerta devuelve el/los nombres de las calles interseccion.
        public DataSet callePuertas(String nmCalle, String numPuerta)
        {
            postgresDAO = new PostgresDAO();
            nmCalle = nmCalle.ToUpper();
            if (validarCalleNumero(nmCalle, numPuerta))
            {
                String query = "select calle_puerta.nm_calle as nm_calle,calle_puerta.nm_esquina as nm_esquina,calle_puerta.punto_x as punto_x,calle_puerta.punto_y as punto_y from vw_calle_puerta calle_puerta ";
                query += " where calle_puerta.nm_calle like '%" + nmCalle + "%'";
                query += " and calle_puerta.num_puerta = '" + numPuerta + "'";
                return postgresDAO.ejecutarQuery(query, "interseccion");
            }
            else if (!encontreCalle(nmCalle))
            {
                // No encontre la calle, devuelvo las posibles calles que quizo ingresar el usuario
                return posiblesCalle(nmCalle, false);
            }
            else
            {
                // No encontre el numero de puerta, devuelvo null
                return null;
            }
        }

        // Dado una calle y un numero de puerta devuelve el/los nombres de las calles interseccion.
        public DataSet calleEsquina(String nmCalle, String nmEsquina)
        {
            nmCalle = nmCalle.ToUpper();
            nmEsquina = nmEsquina.ToUpper();
            postgresDAO = new PostgresDAO();
            if (validarCalleEsquina(nmCalle, nmEsquina))
            {
                // Si encuentro el punto devuelvo el punto en la tabla interseccion 
                String query = "select distinct calle.nombre as nm_calle,esquina.nombre as nm_esquina,x(intersection(calle.the_geom, esquina.the_geom)) as punto_x,y(intersection(calle.the_geom, esquina.the_geom)) as punto_y  from calles calle, calles esquina where intersects(calle.the_geom, esquina.the_geom)";
                query += " and calle.nombre like '%" + nmCalle + "%'";
                query += " and esquina.nombre like '%" + nmEsquina + "%'";
                return postgresDAO.ejecutarQuery(query, "interseccion");
            }
            else if (!encontreCalle(nmCalle))
            {
                // No encontre la calle, devuelvo las posibles calles que quizo ingresar el usuario
                return posiblesCalle(nmCalle, false);
            }
            else if (!encontreCalle(nmEsquina))
            {
                // No encontre la calle, devuelvo las posibles calles que quizo ingresar el usuario
                return posiblesCalle(nmEsquina, true);
            }
            else
            {
                // No encontre ni la calle ni la esquina, devuelvo que no pude interpretar nada de lo que ingreso
                // el usuario
                return null;
            }
        }
    }
}
